• 函数名不能使用关键字(typeof不行但typeOf可以,区分大小写)
  • 标识符就是指变量、函数、属性的名字,或者函数的参数。

    1. 第一个字符必须是一个字母、下划线(_)或者一个美元符号($)
    2. 其他字符可以是字母、下划线、美元符号或者数字
  • 严格模式 在严格模式下ECMAScript3中的一些不确定的行为将得到处理,而且某些不安全的操作也会抛出错误。
// 在整个脚本顶部添加
"use strict";

// 在函数内部上方使用
function deSmoething() {
    "use strict";
    // 函数体
}
  • 关键字和保留字

break,do,instanceof,typeof,case,else,new,var,catch,finally,return,viod,continue,for,switch,while,debugger*,function,this,with,default,if,throw,delete,in,try

  • 在函数中不用var声明变量会变成全局变量,有效但不推荐
function test() {
    message = 'hi'; //全局变量
}
test()
alert(message);     // 'hi'
  • 数据类型 ECMAScript中有5种简单数据类型(也称为基本数据类型):Undefined,Null,Boolean,Number,String 还有1种复杂数据类型Object。本质上Object是由一组无序名值对组成的。
  • typeof返回值"undefined","boolean","string","number","object","function" (typeof null 返回结果是"object") (typeof 是操作符不是函数因此最好不要用括号)

Undefined

// 在使用bar声明变量但未对其加以初始化,这个变量的值就是undefined
var message;
alert(message == undefined); // true
alert(message === undefined); // true

// 下面这个变量并没有声明
// var age
alert(age); // 报错

// 对于尚未声明过的变量,只能执行一项操作,即使用typeof操作符检测其数据类型
// 对未经声明的变量使用delete不会导致错误,但这样没什么意义,而且在严格模式下确实会导致错误
var message;
// var age
alert(typeof message);     // "undefined"
alert(typeof age);        // "undefined"

// 两者都返回了undefined这个结果有其逻辑上的合理性。
// 因为虽然这两种变量从技术角度看有本质区别,但实际上无论对那种变量也不可能执行真正的操作

Null 只有一个值的数据类型(null)

// 从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof 操作符检测null值时会返回"object"的原因
var car = null;
alert(typeof car);         //    "object"

//  如果定义变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值
if (car != null) {
   ...
}

// 实际上 undefined值时派生自null值得,因此ECMA-262规定他们的相等性测试要返回true
alert(null == undefined)     // true
  • Boolean 类型
// 虽然Boolean类型的字面值只有2个,但ECMAScript中所有类型的值都有与这两个Boolean值等价的值。
// 任何非零的数字值(包括无穷大)返回true   0 和 NaN 返回false
var message = "Hello world!";
Boolean(message);  // true
Boolean(0);        // false
Boolean(NaN);      // false
Boolean(Infinity)  // true
Boolean({})        // true
Boolean(null)      // false

Number 类型

// 八进制如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当做十进制数值解析
var num1 = 070;         // 八进制的56
var num2 = 079;         // 无效的八进制数值 解析为79
var num3 = 08;          // 无效的八进制数值 解析为8

// 八进制字面量在严格模式下是无效的,会导致支持该模式的JavaScript引擎抛出错误

// 十六进制字面值的前两位必须是0x 后跟任何十六进制数字 (0~9 和 A~F)其中字母不区分大小写
// 在进行算术计算时,所有八进制和十六进制表示的数值最终都将被转换成十进制数值

// 鉴于JavaScript中数值的保存方式,可以保存正零和负零,两者被认为相等
0 === -0  // true

浮点数。

  • 由于保存浮点数值需要的内存空间是保存证书值得两倍,因此ECMAScript会不失时机的将浮点数值转换为整数值。
  • 浮点数值的最高精度是17位小数,但在进行计算时其精度远远不如证书。例如 0.1 + 0.2 = 0.30000000000000004
  • 这是使用基于IEEE754数值的浮点计算的通病,ECMASscript并非独此一家

数值范围。

  • 由于内存限制,ECMAScript能够表示的最小值保存在Number.MIN_VALUE 在大多数浏览器中这个值时 5e-324
  • 最大值在Number.MAX_VALUE中在大多数浏览器中,这个值是1.7976931348623157e+308;
  • 如果计算中超过了范围将会自动转换成Infinity或-Infinity
  • Infinity无法参加计算
  • 想要确定某个值是不是有穷的(在范围内)使用isFinite()函数
  • 访问Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也可以得到负和正Infinity的值

Infinity + -Infinity // NaN

var res = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(res));     //    false

NaN 即非数值(Not a Number)

  • 是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数值未返回数值的情况
  • 任何涉及NaN的操作都会返回NaN
  • NaN与任何值都不相等,包括NaN本身
  • isNaN() 在接受到一个值之后,会尝试将其转换为数值,而任何不能转换为数值的值都会导致整个函数返回true
isNaN(NaN);     // true
isNaN(10);     // false
isNaN('10');     // false
isNaN('blue'); // ture
isNaN(true);     // false 可以被转换成数值1

// isNaN()也适用于对象,在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,然后确定该方法返回的值是否可以转换为数值
// 如果不能,则基于整个返回值再调用toString()方法,再测试返回值
// 整个过程也是ECMAScript中内置函数和操作符的一般执行流程
var o = {
    valueOf: function() {
        return '10';
    }
}
isNaN(o)    // false 先返回'10' 再转换成数值10

var o2 = {
    valueOf: function() {
        return 'blue';
    }
}
isNaN(o2)    // ture 先返回'blue' 不能转换成数值
  • 数值转换。Number() parseInt() parseFloat()
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number('') // 0

// 如果字符串中包含有效的十六进制格式,例如"0xf" 则将其转换为相同大小的十进制数值
// Number()也适用于对象,在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,然后确定该方法返回的值是否可以转换为数值
// 如果不能,则基于整个返回值再调用toString()方法,再测试返回值

// 由于Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数和浮点数的时候更通常的做法是用parseInt() 和 parseFloat()函数
Number('    0.2') // 0.2
Number('   02blue') // NaN
parseInt('   02blue') // 2
Number('    0.2blue;') // NaN
parseFloat('    0.2blue;') // 0.2

// 在使用parseInt() 解析八进制字面量的字符串时 ECMAScript 3 和 5 存在分歧
// ECMAScript 3 认为56(八进制), ECMAScript 5 认为是70(十进制)
var num = parseInt("070")
// ECMAScript 5 中 parseInt()已经不具有解析八进制值得能力 即使在非严格模式下也是如此
// 可以为这个函数提供第二个参数避免歧义
var num = parseInt("0xAF", 16);  // 175
// 实际上,如果有第二个参数,字符串可以不带“0x”
var num1 = parseInt("AF", 16);   // 175
var num2 = parseInt("AF");   // NaN

// parseFloat() 从第一个字符开始解析,直到末尾,除非解析到遇见一个无效的浮点数为止
// 第一个小数点有效之后就无效
// parseFloat() 只能解析十进制值,因此它没有用的哥参数指定基数的用法,非十进制数值都返回0
// 如果字符串包含的是一个可解析为整数的数,会返回一个整数
parseFloat("1234blue"); // 1234
parseFloat("0xA"); // 0
parseFloat("22.24.5")  // 22.24
parseFloat("0980.5"); // 980.5
parseFloat("3.125e"); // 31250000

String 类型

  • 单引号和双引号都可以用,单首尾必须匹配

字符字面量

  • \n 换行
  • \t 制表
  • \b 退格
  • \r 回车
  • \f 进纸
  • \ 斜杠
  • \' 单引号
  • \" 双引号
  • \xnn 以十六进制代码nn表示的一个字符(其中n为0~F) 例如 \x41表示"A"
  • \unnnn 以十六进制代码nnnn表示一个Unicode字符 (其中n为0~F) 例如 \u03a3 表示希腊字符 sigma 字符串length为1

字符串的特点

  • ECMAScript 中的字符串时不可变的,字符串一旦创建,它们的值就不能改变
  • 要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值得字符串填充该变量
var lang = "Java";
lang = lang + "Script";
/*
 * 实现这个操作的过程
 * 首先创建一个能容纳10个字符的新字符串,然后再这个字符串中填充"Java" 和 "Script"
 * 最后销毁原来的字符串"Java"和字符串"Script",因为这个两个字符串都没用了
 * 这个过程是在后台发生的,而这也是某些旧版本的浏览器 (1.0以下的Firefox IE6等)拼接字符串速度很慢的原因所在
 */

转换为字符串

  • 使用几乎每个值都有的toString()方法 数值、布尔值、对象和字符串值(没错,字符串也有)
  • 在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String()
// toSring()可以传一个参数:输出数值的基数 默认十进制
var num = 10;
num.toString();   // "10"
num.toString(2);  // "1010"
num.toString(8);  // "12"
num.toString(10); // "10"
num.toSring(16);  // "a" 

null.toString();  // 报错
undefined.toString() // 报错 

String(null); // "null"
String(undefined); // "undefined"

Object 类型

// 三种写法等价 但不建议省略小括号
var o = {};
var o = new Object();
var o = new Object;
o.constructor // ƒ Object() { [native code] }  就是Object() 
  • 从技术角度讲,ECMA-262中对象的行为不一定适用于JavaScript中的其他对象。浏览器环境中的对象,比如BOM DOM中的对象都属于宿主中的对象

Object 的每个实例都具有下列属性和方法

  1. constructor: 保存着用于创建当前对象的函数
  2. hasOwnProperty(propertyName): 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中propertyName必须为字符串
  3. isPrototypeOf(object): 用于检查传入的对象是否是当前对象的原型
  4. propertyIsEnumerable(propertyName): 用于检查给定的属性是否能使用for-in语句来枚举
  5. toLocaleString(): 返回对象的字符串表示,该字符串与执行环境的地区对应
  6. toString(): 返回对象的字符串
  7. valueOf(): 返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同
// Baz.prototype, Bar.prototype, Foo.prototype 和 Object.prototype 在 baz 对象的原型链上:
function Foo() {}
function Bar() {}
function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);

var baz = new Baz();

console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

操作符

  • 包括算数操作符、位操作符、关系操作符和相等操作符

递增和递减操作符

  • 前置型 在赋值前改变
  • 后置型 在赋值后改变
var num1 = 2;
var num2 = 20;
var num3 = --num1 + num2;         // 21
var num4 = num1 + num2;           // 21

var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2;         // 22
var num4 = num1 + num2;           // 21
  • 在应用于一个包含有效数字字符串时,先将其转换为数值
  • 在应用于一个包含有效数字的字符串时,将变量的值设置为NaN,字符串变量变成数值变量
  • false,先将其转换为0
  • true,先将其转换为1
  • 浮点数,正常加减1
  • 对象,先调用vlueOf()方法,如果结果是NaN则调用toString()方法后再应用前述规则
var s1 = "2";
var s2 = "z";
var b = false;
bar f = 1.1;
var o = {
    varluOf: function() {
        return -1;
    }
};

s1++;        // 3
s2++;         // NaN
b++;        // 1
f--;         // 0.10000000000000009 浮点运算舍入错误导致
o--;        // -2

一元加减操作符

  • +号会对值自动转换成数值
  • -号会变成负数

位操作符

  • ECMAscript中的所有数值都以IEEE-754 64位格式存储,但位操作符并不直接操作64位值。而是先将64位的值转成32位的整数,然后执行操作 ,最后再将结果转回64位。
  • 32位中的前31位表示整数的值,第32位表示数值的符号,0为正数 1为负数,即为符号位
  • 正数以纯二进制格式存储,没有用到的位用0填充
  • 负数同样二进制,但使用的格式是二进制补码

    • 求数值绝对值的二进制码
    • 求二进制反码 将0换成1 1换成0
    • 得到的二进制反码加1
  • NaN 和 Infinity 应用位操作符时,会被当做0处理,由于64位转存32位导致
  • 按位非(NOT) ~
  • 按位与(AND) &
  • 按位或(OR) |
  • 按位异或(XOR) ^ 两个数值对应位上只有一个1时才返回1,其他情况返回0
  • 左移 << 右侧以0填充
  • 有符号的右移 >> 数值向右移动,保留符号位,左侧以符号位数值填充
  • 无符号的右移 >>> 数值向右移动,不保留符号位 ,左侧以0来填充
var oldvalue = 64;
var newvalue = oldvalue >> 5;        // 二进制10

var oldvalue = 64;
var newvalue = oldvalue >>> 5;      // 二进制 10

var oldvalue = -64;
var newvalue = oldvalue >>> 5;         // 等于十进制的134217726

布尔操作符

  • 逻辑非 !
!"blue"       // false
!0            // true
!NaN          // true
!null         // true
!undefined    // true
!""           // true
!12345        // false
  • 逻辑与 && 逻辑与是短路操作,如果第一个操作数能够决定结果,就不会对第二个操作数求值

    var found = true;
    var result = (found && someUndefinedVariable);     // 抛出错误
    alert(result);         //    这里不会执行
    
    var found = false;
    var result = (found && someUndefindedVariable);     // 不会发生错误
    alert(result);         // false
    • 如果第一个操作数是对象,则返回第二个操作数
    • 如果第二个操作数是对象,则只有在第一个求值结果为true的时候才会返回该对象
    • 如果两个都是对象,则返回第二个操作数
    • 如果有一个操作数是null 则返回null
    • 如果有一个操作数是NaN 则返回NaN
    • 如果有一个操作数是undefined 则返回undefined
    • 如果求值的操作数没有声明会抛出错误
  • 逻辑或 || 短路操作

    var found = true;
    var result = (found || someUndefinedVariable);     // 不会抛出错误
    alert(result);         // true   
    
    var found = false;
    var result = (found || someUndefindedVariable);     // 发生错误
    alert(result);         // 这里不会执行
    • 如果第一个操作数是对象,则返回第一个操作数
    • 如果第一个操作数求值为false,则返回第二个
    • 如果两个都是对象,则返回第一个操作数
    • 如果两个操作数都是null 则返回null
    • 如果两个操作数都是NaN 则返回NaN
    • 如果两个操作数都是undefined 则返回undefined
    • 如果求值的操作数没有声明会抛出错误

乘性操作符

  • 如果参与计算的某个操作数不是数值,后台会先试用Number()转型函数
  • 如果有一个操作数是NaN 则结果是NaN
  • 如果Infinity 与 0 相乘 则结果是NaN
  • 如果Infinity 与 非0相乘 则结果是Infinity 或 -Infinity
  • Infinity * Infinity = Infinity
  • -Infinity * Infinity = -Infinity
  • Infinity / Infinity = NaN
  • 0 / 0 = NaN
  • 非0的有限数 / 0 = Infinity 或 - Infinity
  • 0 / 非0的有限数 = Infinity 或 - Infinity
  • 有限数值 % 0 = NaN
  • Infinity % Infinity = NaN
  • Infinity % 有限数值 = NaN
  • Infinity + -Infinity = NaN
  • +0 + -0 = +0
  • 两个操作数都是字符串 结果是拼接字符串
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串
  • +0 - -0 = +0
var num1 = 5;
var num2 = 10;
var message = "the sum of 5 and 10 is " + num1 + num2;
alert(message);    // "the sum of 5 and 10 is 510"
var message = "the sum of 5 and 10 is " + (num1 + num2);
alert(message);    // "the sum of 5 and 10 is 15"

var res6 = 5 - null;  // 5 null转为0

关系操作符

  • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值进行比较
  • 如果一个操作数是对象,调用valueOf()方法,如果没有则调用toString()
  • 如果一个操作数是布尔值,则先将其转换为数值
  • 任何操作数与NaN进行关系比较,结果都是false
// 字母B的字符编码为66 而字母a的字符编码是97
// 如果要真正按照字母顺序比较字符串,就要相同大小
var result = "Brick" < "alphabet";         // true
var result = "Brick".toLowerCase() < "alphabet".toLowerCase();     // false

// "2" 字符编码是50,而"3"的字符编码是51
var result = "23" < "3";    // true
var result = "23" > 3;    // false

var result = "a" < 3;     // false 因为"a"被转换成了NaN

var result = NaN < 3;    // false
var result = NaN >= 3;    // false

相等操作符

  • 相等和不相等 == , !=

    • 这两个操作符都会先转换操作数(通常称为强制转型),然后再比较他们的相等性
    • 如果有一个操作数是布尔值,先转为1或0再比较
    • 如果一个是字符串另一个是数值,则转换为数值比较
    • 对象先调用valueOf(),用得到的基本类型值按照前面规则进行比较
    • null 和 undefined 相等
    • 比较之前不能讲null和undefined转换成其他任何值
    • 如果有一个操作数是NaN,则相等操作符返回false, NaN也不等于NaN
    • 如果两个操作数都是对象,则比较他们是不是同一个对象,如果都指向同一个对象,则相等操作符返回true
  • 全等和不全等 ===, !==

    • 除了比较之前不转换操作数类型,全等和不全等与相等和不相等操作符没有扫码区别。
null == undefined     // true
null === undefined    // false null 和 undefined是两个基本类型

条件操作符(条件运算符、三元表达式)

variable = boolean_expression ? true_value : false_value;

赋值操作符

  • = 把右侧的值赋给左侧的变量
  • *= /= %= += -=
  • <<= 左移赋值
  • '>>=' 有符号右移赋值
  • '>>>=' 无符号右移赋值
  • 主要目的就是简化赋值操作,使用它们不能带来任何性能的提升

逗号操作符

  • 使用逗号操作符可以在一条语句中执行多个操作
  • 逗号操作符总会返回表达式中的最后一项
var num1=1, num2=2, num3=3;
var num = (5,3,2,4,0);     // num的值为0

语句

if语句

  • 推荐代码块写法

do-while 语句

  • 后测试循环语句,只有在循环体中的代码执行之后,才会测试出口条件
var i = 2;
do {
    i += 2;
    console.log(i);
} while (i < 2);

// 打印 4

while 语句

  • 前测试循环语句,在循环体内的代码被执行之前,就会对出口条件求值
var i = 2;
while (i < 2 ) {
    i += 2;
    console.log(i);
}
// 没有打印

for 语句

  • 前测试循环语句,在循环体内的代码被执行之前,就会对出口条件求值
  • 使用while循环做不到的,使用for循环同样做不到,也就是说for循环只是把循环有关的代码集中在了一个位置
  • 由于ECMAScript中不存在块级作用域,因此在循环内部定义的变量也可以在外部访问到
// 无限循环
for (;;) {
    ...
}

for-in 语句

  • for-in语句是一种精准迭代语句,可以用来枚举对象的属性
  • 循环输出的属性名的顺序是不可预测的,根据浏览器而异
  • 迭代对象为null或undefined会抛出错误, ECMAScript 5 更正了这一行为,不再抛出错误,而是不执行语句

label语句

  • 加标签的语句一般都要与for语句等循环语句配合使用
var i, j;

loop1:
for (i = 0; i < 3; i++) {      //The first for statement is labeled "loop1"
   loop2:
   for (j = 0; j < 3; j++) {   //The second for statement is labeled "loop2"
      if (i == 1 && j == 1) {
         continue loop1;
      }
      console.log("i = " + i + ", j = " + j);
   }
}

// Output is:
//   "i = 0, j = 0"
//   "i = 0, j = 1"
//   "i = 0, j = 2"
//   "i = 1, j = 0"
//   "i = 2, j = 0"
//   "i = 2, j = 1"
//   "i = 2, j = 2"
// Notice how it skips both "i = 1, j = 1" and "i = 1, j = 2"
var itemsPassed = 0;
var i, j;

top:
for (i = 0; i < items.length; i++){
  for (j = 0; j < tests.length; j++){
    if (!tests[j].pass(items[i])){
      continue top;
    }
  }

  itemsPassed++;
}
var i, j;

loop1:
for (i = 0; i < 3; i++) {      //The first for statement is labeled "loop1"
   loop2:
   for (j = 0; j < 3; j++) {   //The second for statement is labeled "loop2"
      if (i == 1 && j == 1) {
         break loop1;
      }
      console.log("i = " + i + ", j = " + j);
   }
}

// Output is:
//   "i = 0, j = 0"
//   "i = 0, j = 1"
//   "i = 0, j = 2"
//   "i = 1, j = 0"
// Notice the difference with the previous continue example

break和continue语句

  • break会立即退出循环
  • continue虽然也会立即退出循环,但会从循环顶部继续执行

with语句 (不推荐使用)

  • 严格模式下不能使用
  • 定义with语句的目的主要是为了简化多次编写同一个对象的工作
  • 大量使用with语句会导致性能下降,同时也会给调式代码造成困难
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

with(location) {
    var qs = search.substring(1);
    var hostName = hostname;
    var url = href;
}

switch语句

  • 通过为每个case后面都添加一个break语句,就可以避免同时执行多个case
  • 假如确实需要混合集中情形,不要忘了添加注释,说明有意省略了break关键字
  • switch语句在比较值时使用的是全等操作,因此"10"不等于10
switch (i) {
    case 25:
        // 合并两种情况
    case 35:
        alert("25 or 35");
        break;
    case 45:
        alert("45");
        break;
    default:
        alert("Other");
}

函数

  • return语句可以不带任何返回值,函数在停止执行后返回undefined
  • 推荐的做法是要么让函数始终都返回一个值要么永远都不要返回值,否则会给调试带来麻烦
  • 严格模式对函数有一些限制,否则会抛出错误

    • 不能把函数命名为eval或arguments
    • 不能把参数命名为eval或arguments
    • 不能出现两个命名参数同名的情况

理解参数

  • ECMAScript函数不介意传递进来多少个参数,也不在乎数据类型,即便定义接受两个参数,调用时也未必要传两个
  • 原因是ECMAScript中的参数在内部是用一个数组来表示的,在函数体内可以通过arguments对象来访问这个参数数组
  • arguments对象只是与数组类似,它并不是Array的实例,可以用方括号语法访问每个元素,也有length属性
  • 函数体内部可以arguments[0],arguments[1]...不显式地使用参数,说明ECMAScript函数的一个重要特点:命名的参数只提供便利性,但不是必需的,解析器不会验证参数名
  • 函数体内可以直接使用arguments[1] = 10 来给参数赋值,但严格模式下会报错不能使用

没有重载

  • ECMAScript函数不能像传统意义上那样实现重载,而在其他语言中可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可
  • 如果在ECMAscript中定义了两个名字相同的函数,则该名字只属于后定义的函数

maroonstar
11 声望3 粉丝